/*
 * Copyright (c) 2011-2022, baomidou (jobob@qq.com).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.sagacity.framework.jdbc.mybatis.meta;

import com.sagacity.framework.annotation.TableName;
import com.sagacity.framework.util.ReflectionKit;
import com.sagacity.framework.util.StringUtil;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.springframework.util.ClassUtils;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * <p>
 * 实体类反射表辅助类
 * </p>
 *
 * @author hubin sjy
 * @since 2016-09-09
 */
public class TableInfoHelper {
    private static final Log logger = LogFactory.getLog(TableInfoHelper.class);

    /**
     * 储存反射类表信息
     */
    private static final Map<String, TableInfo> TABLE_INFO_CACHE_MAPPER_NAME = new ConcurrentHashMap<>();

    /**
     * <p>
     * 实体类反射获取表信息【初始化】
     * </p>
     *
     * @param currentNamespace 命名空间
     * @return 数据库表反射信息
     */
    public static synchronized TableInfo initTableInfo(String currentNamespace) {
        try{
            TableInfo tableInfo = TABLE_INFO_CACHE_MAPPER_NAME.get(currentNamespace);
            if(tableInfo != null){
                return tableInfo;
            }
            // 反射获取类Class
            Class<?> mapperClazz = Class.forName(currentNamespace);
            Type[] genericInterfaces = mapperClazz.getGenericInterfaces();
            ParameterizedType parameterizedType = (ParameterizedType) genericInterfaces[0];
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            Class entityClazz = (Class) actualTypeArguments[0];
            tableInfo = initTableInfo(entityClazz);
            if(tableInfo != null){
                tableInfo.setCurrentNamespace(currentNamespace);
                TABLE_INFO_CACHE_MAPPER_NAME.put(currentNamespace,tableInfo);
                return tableInfo;
            }
        }catch (Exception e){
            logger.warn("非规范currentNamespace,无法解析出表信息");
        }
        return null;
    }

    /**
     * <p>
     * 初始化 表数据库类型,表名,resultMap
     * </p>
     *
     * @param clazz        实体类
     */
    private static synchronized TableInfo initTableInfo(Class<?> clazz) {
        if(clazz == null || clazz == String.class){
            return null;
        }
        TableInfo tableInfo = new TableInfo(clazz);
        // 表名称
        String tableName = getTableName(clazz);
        tableInfo.setTableName(tableName);
        // 字段
        List<Field> allFields = ReflectionKit.getFieldList(ClassUtils.getUserClass(clazz));
        List<TableFieldInfo> fieldInfoList = new ArrayList<>();
        for (Field field : allFields) {
            TableFieldInfo fieldInfo = new TableFieldInfo(field);
            // 区分出主键
            if(fieldInfo.isPrimaryKey()){
                tableInfo.setKeyProperty(fieldInfo.getProperty());
                tableInfo.setKeyColumn(fieldInfo.getColumn());
            }
            // 逻辑删除字段
            if(fieldInfo.isLogicDelete()){
                tableInfo.setLogicColumn(fieldInfo.getColumn());
                tableInfo.setLogicProperty(fieldInfo.getProperty());
            }

            fieldInfoList.add(fieldInfo);
        }
        tableInfo.setFieldList(fieldInfoList);
        return tableInfo;
    }

    /**
     * 获取表名称，有注解@TableName取注解，没有注解取类名称转下滑线
     * @param clazz 实体类
     * @return 表名称
     */
    private static String getTableName(Class<?> clazz) {
        TableName table = clazz.getAnnotation(TableName.class);
        String tableName = StringUtil.toUnderlineCase(clazz.getSimpleName());
        if(table != null && StringUtil.isNotBlank(table.value())){
            tableName = table.value();
        }
        return tableName;
    }


}
